package proxyx

import (
	"cmp"
	"crypto/tls"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"golang.org/x/net/proxy"
)

// GetProxyClient 获取代理客户端
func GetProxyClient(proxyUrl string, clientConfig ClientConfig) (*http.Client, *ProxyConfig, error) {
	if strings.HasPrefix(proxyUrl, "socks5") {
		return GetSocks5ProxyClient(proxyUrl, clientConfig)
	}
	return GetHttpProxyClient(proxyUrl, clientConfig)
}

func GetProxyConfig(proxy string) (*ProxyConfig, error) {
	// "socks5://demo:mobile;us;mobile+communication+company+of+us;;@proxy.test.com:9000",

	param, err := url.Parse(proxy)
	if err != nil {
		return nil, err
	}

	if param.User == nil {
		return &ProxyConfig{
			ProxyUrl: proxy,
			Url:      param.Host,
		}, nil
	}

	pwd, _ := param.User.Password()
	// 对密码进行 URL 编码（有的密码里有@符）
	encodedPassword := url.QueryEscape(pwd)
	proxyURL := fmt.Sprintf("%s://%s:%s@%s", param.Scheme, param.User.Username(), encodedPassword, param.Host)
	socks5Url := fmt.Sprintf("%s:%s@%s", param.User.Username(), encodedPassword, param.Host)
	return &ProxyConfig{
		ProxyUrl:  proxyURL,
		Socks5Url: socks5Url,
		Username:  param.User.Username(),
		Password:  pwd,
		Url:       param.Host,
	}, nil
}

func GetHttpProxyClient(proxyUrl string, clientConfig ClientConfig) (*http.Client, *ProxyConfig, error) {
	uri, err := url.Parse(proxyUrl)
	if err != nil {
		return nil, nil, fmt.Errorf("url.Parse error: %v proxyUrl:%s", err, proxyUrl)
	}

	client := &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyURL(uri),
		},
		Timeout: cmp.Or(clientConfig.Timeout, time.Second*30),
	}
	return client, nil, nil
}

func GetSocks5ProxyClient(proxyUrl string, clientConfig ClientConfig) (*http.Client, *ProxyConfig, error) {
	config, err := GetProxyConfig(proxyUrl)
	if err != nil {
		return nil, nil, err
	}

	auth := proxy.Auth{
		User:     config.Username,
		Password: config.Password,
	}
	// 设置代理
	dialer, err := proxy.SOCKS5("tcp", config.Url, &auth, proxy.Direct)
	if err != nil {
		return nil, nil, fmt.Errorf("socks5 Dialer error: %v config:%+v", err, config)
	}

	// 配置自定义的 Transport 使用自定义的 DialContext
	transport := &http.Transport{
		Dial:              dialer.Dial,
		ForceAttemptHTTP2: cmp.Or(clientConfig.ForceAttemptHTTP2, false),
	}
	// 设置 Transport 的 TLS 配置，忽略证书验证
	transport.TLSClientConfig = &tls.Config{
		InsecureSkipVerify: cmp.Or(clientConfig.IgnoreCert, false), // 忽略证书验证
	}

	// 创建 HTTP 客户端
	client := &http.Client{
		Transport: transport,
		Timeout:   cmp.Or(clientConfig.Timeout, time.Second*30),
	}
	return client, config, nil
}
